#pragma once
#include <Qstring>
#include <QIcon>
#include <QUuid>
#include <QDateTime>
#include <QFile>
#include <QFileInfo>
#include <QDebug>

#include "base.qpb.h"
#include "gateway.qpb.h"
#include "user.qpb.h"
#include "friend.qpb.h"
#include "file.qpb.h"
#include "notify.qpb.h"
#include "speech_recognition.qpb.h"
#include "message_storage.qpb.h"
#include "message_transmit.qpb.h"

namespace model//MVC模式中的model,主要是数据的处理
{
///////////////////
/// 工具函数
/// ///////////////
///
static inline QString getFileName(const QString& path)
{
    QFileInfo fileInfo(path);
    return fileInfo.fileName();
}
//封装一个宏进行日志打印
#define TAG QString("[%1:%2]").arg(model::getFileName(__FILE__),QString::number(__LINE__))
#define LOG() qDebug().noquote()<<TAG


//把时间戳转成格式化的时间
static inline QString formatTime(int64_t timestamp)
{
    //先把时间戳转成QDateTime对象
    QDateTime dateTime = QDateTime::fromSecsSinceEpoch(timestamp);
    return dateTime.toString("MM-dd hh:mm:ss");
}
//获取当前时间戳
static inline int64_t getTime()
{
    return QDateTime::currentSecsSinceEpoch();
}
//根据QByteArray生成一个QIcon对象
static inline QIcon makeIcon(const QByteArray& byteArray)
{
    QPixmap pixmap;
    pixmap.loadFromData(byteArray);
    return QIcon(pixmap);
}
//从指定文件中读取所有的二进制内容，放到一个QByteArray对象中
static inline QByteArray loadFileToByteArray(const QString& path)
{
    QFile file(path);
    bool ok = file.open(QFile::ReadOnly);
    if(!ok)
    {
        LOG()<<"打开文件失败";
        return QByteArray();
    }
    QByteArray content = file.readAll();
    file.close();
    return content;
}
//把QByteArray对象写入到指定文件中
static inline void writeByteArrayToFile(const QString& path,const QByteArray& content)
{
    QFile file(path);
    bool ok = file.open(QFile::WriteOnly);
    if(!ok)
    {
        LOG()<<"打开文件失败";
        return;
    }
    file.write(content);
    file.flush();
    file.close();
}


/////////////////////////////////////////////////////
/// 用户信息
/////////////////////////////////////
class UserInfo
{
public:
    QString userId = "";//用户id
    QString nickname = "";//用户昵称
    QString description = "";//用户签名
    QString phone = "";//用户手机号、
    QIcon avatar;//用户头像

    // 从 protobuffer 的 UserInfo 对象, 转成当前代码的 UserInfo 对象
    void load(const wsll::UserInfo& userInfo)//wsll命名空间的UserInfo，也就是proto生成的UserInfo
    {
        this->userId = userInfo.userId();//这个方法也是proto自动生成的  原本的proto文件是 user_id 的下划线，但是proto生成的方法自动换成是userId这样的驼峰
        this->nickname = userInfo.nickname();
        this->phone = userInfo.phone();
        this->description = userInfo.description();
        if (userInfo.avatar().isEmpty()) {
            // 使用默认头像即可
            this->avatar = QIcon(":/resource/image/defaultUser.png");
        } else {
            this->avatar = makeIcon(userInfo.avatar());//之前写的工具函数的作用就体现出来了
        }
    }
};

/////////////////////////////////////
/// 消息类型
/// ////////////////////////////////
enum MessageType
{
    TEXT_TYPE,//文本消息
    IMAGE_TYPE,//图片消息
    FILE_TYPE,//文件消息
    SPEECH_TYPE//语音消息
};
//////////////
/// 消息信息
/// ///////////////////
class Message
{
public:
    QString messageId = "";//消息id
    QString chatSessionId="";//会话id,用来标识这条消息属于哪个会话，和下面的ChatSessionInfo中的chatSessionId对应
    QString time = "";//消息发送时间，通过格式化时间的方式来表示，06-07 12:00:00
    MessageType messageType = TEXT_TYPE;//消息类型，包括文本、图片、语音、文件等
    UserInfo sender;//消息发送者
    QByteArray content;//消息内容，文本消息就是文本内容(后面可以转成Qstring），图片消息就是图片的二进制数据，语音消息就是语音的二进制数据，文件消息就是文件的二进制数据
    QString fileId = ""; //文件的身份标识，如果消息类型是文件图片语音时这个id才有用。如果是文本消息，那么这个id就是空。在获取消息列表之后，第一步只是拿到fileid，然后再通过这个fileid去服务器获取文件的二进制数据
    QString fileName = "";//文件名，如果消息类型是文件时这个id才有用。也就是只显示文件消息的文件名.虽然图片和语音也是文件，但这两者不显示

    //工厂函数，用来创建一个Message对象
    static Message makeMessage(MessageType messageType,const QString& chatSessionId,const UserInfo& sender,
                               const QByteArray& content,const QString& extraInfo)//extraInfo是在消息类型是文件时，作为“文件名的补充
    {
        if(messageType==TEXT_TYPE)
        {
            return makeTextMessage(chatSessionId,sender,content);
        }
        else if(messageType == IMAGE_TYPE)
        {
            return makeImageMessage(chatSessionId,sender,content);
        }
        else if(messageType == FILE_TYPE)
        {
            return makeFileMessage(chatSessionId,sender,content,extraInfo);
        }
        else if(messageType == SPEECH_TYPE)
        {
            return makeSpeechMessage(chatSessionId,sender,content);
        }
        else
        {
            //触发了未知的消息类型
            return Message();
        }
    }

    void load(const wsll::MessageInfo& messageInfo) {
        this->messageId = messageInfo.messageId();
        this->chatSessionId = messageInfo.chatSessionId();
        this->time = formatTime(messageInfo.timestamp());//我们约定的时间戳级别是秒级别，所以这里直接用timestamp方法
        this->sender.load(messageInfo.sender());

        // 设置消息类型
        auto type = messageInfo.message().messageType();//注意pb中的枚举类型和我们的枚举类型是不一样的，所以要进行转换
        if (type == wsll::MessageTypeGadget::MessageType::STRING)
        {
            this->messageType = TEXT_TYPE;
            this->content = messageInfo.message().stringMessage().content().toUtf8();
        }
        else if (type == wsll::MessageTypeGadget::MessageType::IMAGE)
        {
            this->messageType = IMAGE_TYPE;
            if (messageInfo.message().imageMessage().hasImageContent())//由于字段是optional的，因此要判断是否有值
            {
                this->content = messageInfo.message().imageMessage().imageContent();
            }
            if (messageInfo.message().imageMessage().hasFileId())
            {
                this->fileId = messageInfo.message().imageMessage().fileId();
            }
        }
        else if (type == wsll::MessageTypeGadget::MessageType::FILE)
        {
            this->messageType = FILE_TYPE;
            if (messageInfo.message().fileMessage().hasFileContents())
            {
                this->content = messageInfo.message().fileMessage().fileContents();
            }
            if (messageInfo.message().fileMessage().hasFileId())
            {
                this->fileId = messageInfo.message().fileMessage().fileId();
            }
            this->fileName = messageInfo.message().fileMessage().fileName();
        }
        else if (type == wsll::MessageTypeGadget::MessageType::SPEECH)
        {
            this->messageType = SPEECH_TYPE;
            if (messageInfo.message().speechMessage().hasFileContents())
            {
                this->content = messageInfo.message().speechMessage().fileContents();
            }
            if (messageInfo.message().speechMessage().hasFileId())
            {
                this->fileId = messageInfo.message().speechMessage().fileId();
            }
        }
        else
        {
            // 错误的类型, 啥都不做了, 只是打印一个日志
            LOG() << "非法的消息类型! type=" << type;
        }
    }

private:
    //通过这个方法生成唯一的消息id
    static QString makeId()
    {
        return "M"+QUuid::createUuid().toString().sliced(25,12);//能生成一个全球唯一的身份标识
    }

    static Message makeTextMessage(const QString& chatSessionId,const UserInfo& sender,const QByteArray& content)
    {
        Message message;
        message.messageId=makeId();
        message.chatSessionId=chatSessionId;
        message.sender=sender;

        message.time=formatTime(getTime());
        message.content=content;
        message.messageType=TEXT_TYPE;
        message.fileId = "";
        message.fileName = "";
        return message;
    }
    static Message makeImageMessage(const QString& chatSessionId,const UserInfo& sender,const QByteArray& content)
    {
        Message message;
        message.messageId=makeId();
        message.chatSessionId=chatSessionId;
        message.sender=sender;
        message.time = formatTime(getTime());
        message.content=content;
        message.messageType=IMAGE_TYPE;
        message.fileId = "";//后续使用时再设置
        message.fileName = "";
        return message;

    }
    static Message makeFileMessage(const QString& chatSessionId,const UserInfo& sender,const QByteArray& content,const QString& fileName)
    {
        Message message;
        message.messageId=makeId();
        message.chatSessionId=chatSessionId;
        message.sender=sender;
        message.time = formatTime(getTime());
        message.content=content;
        message.messageType=FILE_TYPE;
        message.fileId = "";//后续使用时再设置
        message.fileName = fileName;
        return message;
    }
    static Message makeSpeechMessage(const QString& chatSessionId,const UserInfo& sender,const QByteArray& content)
    {
        Message message;
        message.messageId=makeId();
        message.chatSessionId=chatSessionId;
        message.sender=sender;
        message.time = formatTime(getTime());
        message.content=content;
        message.messageType=SPEECH_TYPE;;
        message.fileId = "";//后续使用时再设置
        message.fileName = "";
        return message;
    }
};

/////////////////////////////////
/////会话信息
//////////////////////////////////////

class ChatSessionInfo
{
public:
    QString chatSessionId = "";//会话id
    QString chatSessionName = "";//会话名称,指消息聊天窗口的标题 。比如如果是单聊，那么会话名字就是对方昵称；如果是群聊，那么会话名字就是群名
    Message lastMessage;//最后一条消息,这个消息将在会话列表中展示，用来进行提示
    QIcon avatar;//会话头像,如果是单聊，那么这个头像就是对方头像；如果是群聊，那么这个头像就是群头像
    QString userId = "";//如果会话是单聊，那么就是对方的id，如果是群聊，那么就设为“”后续通过其他方式处理

    void load(const wsll::ChatSessionInfo& chatSessionInfo)
    {
        this->chatSessionId = chatSessionInfo.chatSessionId();
        this->chatSessionName = chatSessionInfo.chatSessionName();
        if (chatSessionInfo.hasSingleChatFriendId())
        {
            this->userId = chatSessionInfo.singleChatFriendId();
        }
        if (chatSessionInfo.hasPrevMessage())
        {
            lastMessage.load(chatSessionInfo.prevMessage());
        }
        if (chatSessionInfo.hasAvatar() && !chatSessionInfo.avatar().isEmpty())
        {
            // 已经有头像了, 直接设置这个头像
            this->avatar = makeIcon(chatSessionInfo.avatar());
        } else
        {
            // 如果没有头像, 则根据当前会话是单聊还是群聊, 使用不同的默认头像.
            if (userId != "")
            {
                // 单聊
                this->avatar = QIcon(":/resource/image/defaultUser.png");
            } else {
                // 群聊
                this->avatar = QIcon(":/resource/image/groupUser.jpg");
            }
        }
    }
};
















}//end model
